﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using WizardControl.Views;

namespace WizardControl
{
    /// <summary>
    /// Interaction logic for Wizard.xaml
    /// </summary>
    public partial class Wizard : UserControl
    {
        /// <summary>
        /// Constuctor
        /// </summary>
        public Wizard()
        {
            InitializeComponent();

            VisitedPages = new Stack<int>();
            m_Pages = new List<IPage>();
        }

        /// <summary>
        /// Event raised then to users of this control when the wizard is finished
        /// </summary>
        public event WizardFinishedEvent WizardFinished;

        /// <summary>
        /// Delegate for the WizardFinished event
        /// </summary>
        /// <param name="sender">Who raised the event</param>
        /// <param name="outputData">Data out put from the final stage of the wizard</param>
        public delegate void WizardFinishedEvent(object sender, object outputData);

        /// <summary>
        /// Event raised to users of this control when the wizard is cancelled
        /// </summary>
        public event WizardCancelledEvent WizardCancelled;

        /// <summary>
        /// Delegate for the WizardCancelled event
        /// </summary>
        /// <param name="sender">Who raised the event</param>
        /// <param name="args">Details sent to the subscriber</param>
        public delegate void WizardCancelledEvent(object sender, EventArgs args);

        /// <summary>
        /// Constant to use to signal we want to jump to the finish
        /// </summary>
        public const int FINISH_PAGE = -1;

        /// <summary>
        /// Constant to signal that we want the next sequantial page loaded
        /// </summary>
        public const int NEXT_PAGE = 0;

        /// <summary>
        /// Object contain output data when the user clicks Finish
        /// </summary>
        public object Output
        {
            get;
            set;
        }

        /// <summary>
        /// The currently active page
        /// </summary>
        public IPage ActivePage
        {
            get
            {
                IPage result = null;

                if (ccPage.Content is IPage)
                {
                    result = (IPage)ccPage.Content;
                }

                return result;
            }
            set
            {
                // If the supplied value exists in the Pages list then load and display it
                if ((value != null) && (m_Pages.Contains(value)))
                {
                    // Before changing the page call the PageDeactivated method on the current page
                    IPage currentPage = ActivePage;
                    if (currentPage != null)
                    {
                        currentPage.PageDeactivated();
                    }

                    ccPage.Content = value;
                    SetUpPage();

                    // Call PageActivated on the new page
                    value.PageActivated();
                }
            }
        }

        /// <summary>
        /// Add a wizard page
        /// </summary>
        /// <param name="page">The wizard page to add</param>
        public void AddPage(IPage page)
        {
            // Add wizard page
            m_Pages.Add(page);

            // Link the wizard to the page
            page.ParentWizard = this;
        }

        /// <summary>
        /// Remove a page from the wizrd
        /// </summary>
        /// <param name="page">The page to remove</param>
        /// <returns>True id successful</returns>
        public bool RemovePage(IPage page)
        {
            return m_Pages.Remove(page);
        }

        /// <summary>
        /// Clear out the wizrd
        /// </summary>
        public void Clear()
        {
            m_Pages.Clear();
        }

        /// <summary>
        /// Navigate directly to a specified page, if it exists
        /// </summary>
        /// <param name="pageNumber">The page number to goto</param>
        /// <param name="transferOutoutData">If true then the output data from the current page is transferred to
        /// the input data for the page we are moving to</param>
        public void GotoPage(int pageNumber, bool transferOutoutData)
        {
            // Search for the page in the list of loaded pages
            var pageToShow = (from page in m_Pages
                              where page.PageNumber() == pageNumber
                              select page).FirstOrDefault();

            // If we have a page and it is a valid user control, load it
            if (pageToShow != null)
            {
                if (transferOutoutData)
                {
                    pageToShow.InputData = ActivePage == null ? null : ActivePage.OutputData;
                }

                ActivePage = pageToShow;
            }
        }

        /// <summary>
        /// Go to the next page in the wizard
        /// </summary>
        public void GotoNextPage()
        {
            IPage currentPage = ActivePage;

            // Validate the current page
            if (currentPage.ValidatePage())
            {
                int currentPageNumber = currentPage.PageNumber();
                int nextPageNumber = currentPage.NextPageNumber();
                object nextPageInputData = currentPage.OutputData;

                // Validation passed so proceed to the next page or raise an event to say we are finished/cancelled etc
                if (nextPageNumber == NEXT_PAGE)
                {
                    // Move to the next page
                    var nextPage = (from page in m_Pages
                                    where page.NextPageNumber() > currentPageNumber
                                    orderby page.PageNumber()
                                    select page).FirstOrDefault();

                    nextPage.InputData = nextPageInputData;
                    ActivePage = nextPage;
                }
                else
                {
                    // Just move to the specified page number
                    GotoPage(nextPageNumber, true);
                }

                // Put the current page number of the VisitedPages stack
                VisitedPages.Push(currentPageNumber);
            }
        }

        /// <summary>
        /// Goto the previous page in the wizard
        /// </summary>
        public void GotoPreviousPage()
        {
            if (VisitedPages.Count > 0)
            {
                // Get the page number from the top of the VisitedPages stack
                int prevPageNumber = VisitedPages.Pop();

                // Navigate to that page but don't transfer any data
                GotoPage(prevPageNumber, false);
            }
        }

        /// <summary>
        /// Start the wizard off with data and a starting page
        /// </summary>
        /// <param name="input">Initial data supplied to the wizard</param>
        /// <param name="startPage">The page to start from</param>
        public void StartWizard(object input, int startPage)
        {
            GotoPage(startPage, false);

            if (ActivePage != null)
            {
                ActivePage.InputData = input;
            }
        }

        /// <summary>
        /// A stack of visited pages
        /// </summary>
        private Stack<int> VisitedPages
        {
            get;
            set;
        }

        /// <summary>
        /// Update the Navigation buttons, caption etc for the current page
        /// </summary>
        private void SetUpPage()
        {
            IPage currentPage = ActivePage;

            if (currentPage != null)
            {
                btnFinish.IsEnabled = currentPage.AllowFinish();
                btnForward.IsEnabled = currentPage.AllowForward();
                btnBack.IsEnabled = currentPage.AllowBack();
                btnCancel.IsEnabled = currentPage.AllowCancel();

                lblPageCaption.Content = currentPage.PageCaption();
            }
        }

        /// <summary>
        /// Event raised when the Forward button is clicked
        /// </summary>
        /// <param name="sender">Who raised the event</param>
        /// <param name="e">Details about the event</param>
        private void btnForward_Click(object sender, RoutedEventArgs e)
        {
            GotoNextPage();
        }

        /// <summary>
        /// Event raised when the back button is clicked
        /// </summary>
        /// <param name="sender">Who raised the event</param>
        /// <param name="e">Details about the event</param>
        private void btnBack_Click(object sender, RoutedEventArgs e)
        {
            GotoPreviousPage();
        }

        /// <summary>
        /// Event raised when cancel is clicked
        /// </summary>
        /// <param name="sender">Who raised the event</param>
        /// <param name="e">Details about the event</param>
        private void btnCancel_Click(object sender, RoutedEventArgs e)
        {
            if (WizardCancelled != null)
            {
                WizardCancelled(this, new EventArgs());
            }
        }

        /// <summary>
        /// Event raised when the Finish button is clicked
        /// </summary>
        /// <param name="sender">Who raised the event</param>
        /// <param name="e">Details about the event</param>
        private void btnFinish_Click(object sender, RoutedEventArgs e)
        {
            if (WizardFinished != null)
            {
                Output = ActivePage.OutputData;
                WizardFinished(this, Output);
            }
        }

        /// <summary>
        /// A list of pages currently loaded into the wizards
        /// </summary>
        private List<IPage> m_Pages;
    }
}
